home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / fileutil.13 / fileutil / fileutils-3.13 / src / rm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-24  |  14.1 KB  |  545 lines

  1. /* `rm' file deletion utility for GNU.
  2.    Copyright (C) 88, 90, 91, 94, 95, 1996 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software Foundation,
  16.    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* Written by Paul Rubin, David MacKenzie, and Richard Stallman.  */
  19.  
  20. #include <config.h>
  21. #include <stdio.h>
  22. #include <getopt.h>
  23. #include <sys/types.h>
  24.  
  25. #include "system.h"
  26. #include "error.h"
  27.  
  28. #ifdef D_INO_IN_DIRENT
  29. # define D_INO(dp) ((dp)->d_ino)
  30. #else
  31. /* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
  32. # define D_INO(dp) 1
  33. #endif
  34.  
  35. /* An element in a stack of pointers into `pathname'.
  36.    `pathp' points to where in `pathname' the terminating '\0' goes
  37.    for this level's directory name.  */
  38. struct pathstack
  39. {
  40.   struct pathstack *next;
  41.   char *pathp;
  42.   ino_t inum;
  43. };
  44.  
  45. char *basename ();
  46. char *stpcpy ();
  47. char *xmalloc ();
  48. char *xrealloc ();
  49. int euidaccess ();
  50. int yesno ();
  51. void strip_trailing_slashes ();
  52.  
  53. static int clear_directory __P ((struct stat *statp));
  54. static int duplicate_entry __P ((struct pathstack *stack, ino_t inum));
  55. static int remove_dir __P ((struct stat *statp));
  56. static int remove_file __P ((struct stat *statp));
  57. static int rm __P ((void));
  58. static void usage __P ((int status));
  59.  
  60. /* Name this program was run with.  */
  61. char *program_name;
  62.  
  63. /* Linked list of pathnames of directories in progress in recursive rm.
  64.    The entries actually contain pointers into `pathname'.
  65.    `pathstack' is the current deepest level.  */
  66. static struct pathstack *pathstack = NULL;
  67.  
  68. /* Path of file now being processed; extended as necessary.  */
  69. static char *pathname;
  70.  
  71. /* Number of bytes currently allocated for `pathname';
  72.    made larger when necessary, but never smaller.  */
  73. static int pnsize;
  74.  
  75. /* If nonzero, display the name of each file removed.  */
  76. static int verbose;
  77.  
  78. /* If nonzero, ignore nonexistant files.  */
  79. static int ignore_missing_files;
  80.  
  81. /* If nonzero, recursively remove directories.  */
  82. static int recursive;
  83.  
  84. /* If nonzero, query the user about whether to remove each file.  */
  85. static int interactive;
  86.  
  87. /* If nonzero, remove directories with unlink instead of rmdir, and don't
  88.    require a directory to be empty before trying to unlink it.
  89.    Only works for the super-user.  */
  90. static int unlink_dirs;
  91.  
  92. /* If nonzero, stdin is a tty.  */
  93. static int stdin_tty;
  94.  
  95. /* If nonzero, display usage information and exit.  */
  96. static int show_help;
  97.  
  98. /* If nonzero, print the version on standard output and exit.  */
  99. static int show_version;
  100.  
  101. static struct option const long_opts[] =
  102. {
  103.   {"directory", no_argument, &unlink_dirs, 1},
  104.   {"force", no_argument, NULL, 'f'},
  105.   {"interactive", no_argument, NULL, 'i'},
  106.   {"recursive", no_argument, &recursive, 1},
  107.   {"verbose", no_argument, &verbose, 1},
  108.   {"help", no_argument, &show_help, 1},
  109.   {"version", no_argument, &show_version, 1},
  110.   {NULL, 0, NULL, 0}
  111. };
  112.  
  113. int
  114. main (int argc, char **argv)
  115. {
  116.   int err = 0;
  117.   int c;
  118.  
  119.   program_name = argv[0];
  120.   setlocale (LC_ALL, "");
  121.   bindtextdomain (PACKAGE, LOCALEDIR);
  122.   textdomain (PACKAGE);
  123.  
  124.   verbose = ignore_missing_files = recursive = interactive
  125.     = unlink_dirs = 0;
  126.   pnsize = 256;
  127.   pathname = xmalloc (pnsize);
  128.  
  129.   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
  130.     {
  131.       switch (c)
  132.     {
  133.     case 0:        /* Long option.  */
  134.       break;
  135.     case 'd':
  136.       unlink_dirs = 1;
  137.       break;
  138.     case 'f':
  139.       interactive = 0;
  140.       ignore_missing_files = 1;
  141.       break;
  142.     case 'i':
  143.       interactive = 1;
  144.       ignore_missing_files = 0;
  145.       break;
  146.     case 'r':
  147.     case 'R':
  148.       recursive = 1;
  149.       break;
  150.     case 'v':
  151.       verbose = 1;
  152.       break;
  153.     default:
  154.       usage (1);
  155.     }
  156.     }
  157.  
  158.   if (show_version)
  159.     {
  160.       printf ("rm - %s\n", PACKAGE_VERSION);
  161.       exit (0);
  162.     }
  163.  
  164.   if (show_help)
  165.     usage (0);
  166.  
  167.   if (optind == argc)
  168.     {
  169.       if (ignore_missing_files)
  170.     exit (0);
  171.       else
  172.     {
  173.       error (0, 0, _("too few arguments"));
  174.       usage (1);
  175.     }
  176.     }
  177.  
  178.   stdin_tty = isatty (STDIN_FILENO);
  179.  
  180.   for (; optind < argc; optind++)
  181.     {
  182.       int len;
  183.  
  184.       /* Stripping slashes is harmless for rmdir;
  185.      if the arg is not a directory, it will fail with ENOTDIR.  */
  186.       strip_trailing_slashes (argv[optind]);
  187.       len = strlen (argv[optind]);
  188.       if (len + 1 > pnsize)
  189.     {
  190.       free (pathname);
  191.       pnsize = 2 * (len + 1);
  192.       pathname = xmalloc (pnsize);
  193.     }
  194.       strcpy (pathname, argv[optind]);
  195.       err += rm ();
  196.     }
  197.  
  198.   exit (err > 0);
  199. }
  200.  
  201. /* Remove file or directory `pathname' after checking appropriate things.
  202.    Return 0 if `pathname' is removed, 1 if not.  */
  203.  
  204. static int
  205. rm (void)
  206. {
  207.   struct stat path_stats;
  208.   char *base = basename (pathname);
  209.  
  210.   if (base[0] == '.' && (base[1] == '\0'
  211.                  || (base[1] == '.' && base[2] == '\0')))
  212.     {
  213.       error (0, 0, _("cannot remove `.' or `..'"));
  214.       return 1;
  215.     }
  216.  
  217.   if (lstat (pathname, &path_stats)
  218.       /* The following or-clause is solely for systems like SunOS 4.1.3
  219.          with (broken) lstat that interpret a zero-length file name
  220.      argument as something meaningful.  For such systems, manually
  221.      set errno to ENOENT.  */
  222.       || (pathname[0] == '\0' && (errno = ENOENT)))
  223.     {
  224.       if (errno == ENOENT && ignore_missing_files)
  225.     return 0;
  226.       error (0, errno, "%s", pathname);
  227.       return 1;
  228.     }
  229.  
  230.   if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
  231.     return remove_dir (&path_stats);
  232.   else
  233.     return remove_file (&path_stats);
  234. }
  235.  
  236. /* Query the user if appropriate, and if ok try to remove the
  237.    non-directory `pathname', which STATP contains info about.
  238.    Return 0 if `pathname' is removed, 1 if not.  */
  239.  
  240. static int
  241. remove_file (struct stat *statp)
  242. {
  243.   if (!ignore_missing_files && (interactive || stdin_tty)
  244.       && euidaccess (pathname, W_OK)
  245. #ifdef S_ISLNK
  246.       && !S_ISLNK (statp->st_mode)
  247. #endif
  248.       )
  249.     {
  250.       fprintf (stderr, _("%s: remove %s`%s', overriding mode %04o? "),
  251.            program_name,
  252.            S_ISDIR (statp->st_mode) ? _("directory ") : "",
  253.            pathname,
  254.            (unsigned int) (statp->st_mode & 07777));
  255.       if (!yesno ())
  256.     return 1;
  257.     }
  258.   else if (interactive)
  259.     {
  260.       fprintf (stderr, _("%s: remove %s`%s'? "), program_name,
  261.            S_ISDIR (statp->st_mode) ? _("directory ") : "",
  262.            pathname);
  263.       if (!yesno ())
  264.     return 1;
  265.     }
  266.  
  267.   if (verbose)
  268.     printf ("%s\n", pathname);
  269.  
  270.   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
  271.     {
  272.       error (0, errno, "%s", pathname);
  273.       return 1;
  274.     }
  275.   return 0;
  276. }
  277.  
  278. /* If not in recursive mode, print an error message and return 1.
  279.    Otherwise, query the user if appropriate, then try to recursively
  280.    remove directory `pathname', which STATP contains info about.
  281.    Return 0 if `pathname' is removed, 1 if not.  */
  282.  
  283. static int
  284. remove_dir (struct stat *statp)
  285. {
  286.   int err;
  287.  
  288.   if (!recursive)
  289.     {
  290.       error (0, 0, _("%s: is a directory"), pathname);
  291.       return 1;
  292.     }
  293.  
  294.   if (!ignore_missing_files && (interactive || stdin_tty)
  295.       && euidaccess (pathname, W_OK))
  296.     {
  297.       fprintf (stderr,
  298.            _("%s: descend directory `%s', overriding mode %04o? "),
  299.            program_name, pathname,
  300.            (unsigned int) (statp->st_mode & 07777));
  301.       if (!yesno ())
  302.     return 1;
  303.     }
  304.   else if (interactive)
  305.     {
  306.       fprintf (stderr, _("%s: descend directory `%s'? "),
  307.            program_name, pathname);
  308.       if (!yesno ())
  309.     return 1;
  310.     }
  311.  
  312.   if (verbose)
  313.     printf ("%s\n", pathname);
  314.  
  315.   err = clear_directory (statp);
  316.  
  317.   if (interactive)
  318.     {
  319.       if (err)
  320.     fprintf (stderr, _("%s: remove directory `%s' (might be nonempty)? "),
  321.          program_name, pathname);
  322.       else
  323.     fprintf (stderr, _("%s: remove directory `%s'? "),
  324.          program_name, pathname);
  325.       if (!yesno ())
  326.     return 1;
  327.     }
  328.  
  329.   if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
  330.     {
  331.       error (0, errno, "%s", pathname);
  332.       return 1;
  333.     }
  334.   return 0;
  335. }
  336.  
  337. /* Read directory `pathname' and remove all of its entries,
  338.    avoiding use of chdir.
  339.    On entry, STATP points to the results of stat on `pathname'.
  340.    Return 0 for success, error count for failure.
  341.    Upon return, `pathname' will have the same contents as before,
  342.    but its address might be different; in that case, `pnsize' will
  343.    be larger, as well.  */
  344.  
  345. static int
  346. clear_directory (struct stat *statp)
  347. {
  348.   DIR *dirp;
  349.   struct dirent *dp;
  350.   char *name_space;        /* Copy of directory's filenames.  */
  351.   char *namep;            /* Current entry in `name_space'.  */
  352.   unsigned name_size;        /* Bytes allocated for `name_space'.  */
  353.   int name_length;        /* Length of filename in `namep' plus '\0'.  */
  354.   int pathname_length;        /* Length of `pathname'.  */
  355.   ino_t *inode_space;        /* Copy of directory's inodes.  */
  356.   ino_t *inodep;        /* Current entry in `inode_space'.  */
  357.   unsigned n_inodes_allocated;    /* There is space for this many inodes
  358.                       in `inode_space'.  */
  359.   int err = 0;            /* Return status.  */
  360.   struct pathstack pathframe;    /* New top of stack.  */
  361.   struct pathstack *pp;        /* Temporary.  */
  362.  
  363.   name_size = statp->st_size;
  364.   name_space = (char *) xmalloc (name_size);
  365.  
  366.   n_inodes_allocated = (statp->st_size + sizeof (ino_t) - 1) / sizeof (ino_t);
  367.   inode_space = (ino_t *) xmalloc (n_inodes_allocated * sizeof (ino_t));
  368.  
  369.   do
  370.     {
  371.       namep = name_space;
  372.       inodep = inode_space;
  373.  
  374.       errno = 0;
  375.       dirp = opendir (pathname);
  376.       if (dirp == NULL)
  377.     {
  378.       if (errno != ENOENT || !ignore_missing_files)
  379.         {
  380.           error (0, errno, "%s", pathname);
  381.           err = 1;
  382.         }
  383.       free (name_space);
  384.       free (inode_space);
  385.       return err;
  386.     }
  387.  
  388.       while ((dp = readdir (dirp)) != NULL)
  389.     {
  390.       /* Skip "." and "..".  */
  391.       if (dp->d_name[0] != '.'
  392.           || (dp->d_name[1] != '\0'
  393.           && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
  394.         {
  395.           unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
  396.  
  397.           if (size_needed > name_size)
  398.         {
  399.           char *new_name_space;
  400.  
  401.           while (size_needed > name_size)
  402.             name_size += 1024;
  403.  
  404.           new_name_space = xrealloc (name_space, name_size);
  405.           namep += new_name_space - name_space;
  406.           name_space = new_name_space;
  407.         }
  408.           namep = stpcpy (namep, dp->d_name) + 1;
  409.  
  410.           if (inodep == inode_space + n_inodes_allocated)
  411.         {
  412.           ino_t *new_inode_space;
  413.  
  414.           n_inodes_allocated += 1024;
  415.           new_inode_space = (ino_t *) xrealloc (inode_space,
  416.                     n_inodes_allocated * sizeof (ino_t));
  417.           inodep += new_inode_space - inode_space;
  418.           inode_space = new_inode_space;
  419.         }
  420.           *inodep++ = D_INO (dp);
  421.         }
  422.     }
  423.       *namep = '\0';
  424.       if (CLOSEDIR (dirp))
  425.     {
  426.       error (0, errno, "%s", pathname);
  427.       err = 1;
  428.     }
  429.  
  430.       pathname_length = strlen (pathname);
  431.  
  432.       for (namep = name_space, inodep = inode_space; *namep != '\0';
  433.        namep += name_length, inodep++)
  434.     {
  435.       name_length = strlen (namep) + 1;
  436.  
  437.       /* Handle arbitrarily long filenames.  */
  438.       if (pathname_length + 1 + name_length > pnsize)
  439.         {
  440.           char *new_pathname;
  441.  
  442.           pnsize = (pathname_length + 1 + name_length) * 2;
  443.           new_pathname = xrealloc (pathname, pnsize);
  444.           /* Update all pointers in the stack to use the new area.  */
  445.           for (pp = pathstack; pp != NULL; pp = pp->next)
  446.         pp->pathp += new_pathname - pathname;
  447.           pathname = new_pathname;
  448.         }
  449.  
  450.       /* Add a new frame to the top of the path stack.  */
  451.       pathframe.pathp = pathname + pathname_length;
  452.       pathframe.inum = *inodep;
  453.       pathframe.next = pathstack;
  454.       pathstack = &pathframe;
  455.  
  456.       /* Append '/' and the filename to current pathname, take care of
  457.          the file (which could result in recursive calls), and take
  458.          the filename back off.  */
  459.  
  460.       *pathstack->pathp = '/';
  461.       strcpy (pathstack->pathp + 1, namep);
  462.  
  463.       /* If the i-number has already appeared, there's an error.  */
  464.       if (duplicate_entry (pathstack->next, pathstack->inum))
  465.         err++;
  466.       else if (rm ())
  467.         err++;
  468.  
  469.       *pathstack->pathp = '\0';
  470.       pathstack = pathstack->next;    /* Pop the stack.  */
  471.     }
  472.     }
  473.   /* Keep trying while there are still files to remove.  */
  474.   while (namep > name_space && err == 0);
  475.  
  476.   free (name_space);
  477.   free (inode_space);
  478.   return err;
  479. }
  480.  
  481. /* If STACK does not already have an entry with the same i-number as INUM,
  482.    return 0. Otherwise, ask the user whether to continue;
  483.    if yes, return 1, and if no, exit.
  484.    This assumes that no one tries to remove filesystem mount points;
  485.    doing so could cause duplication of i-numbers that would not indicate
  486.    a corrupted file system.  */
  487.  
  488. static int
  489. duplicate_entry (struct pathstack *stack, ino_t inum)
  490. {
  491. #ifdef D_INO_IN_DIRENT
  492.   struct pathstack *p;
  493.  
  494.   for (p = stack; p != NULL; p = p->next)
  495.     {
  496.       if (p->inum == inum)
  497.     {
  498.       fprintf (stderr, _("\
  499. %s: WARNING: Circular directory structure.\n\
  500. This almost certainly means that you have a corrupted file system.\n\
  501. NOTIFY YOUR SYSTEM MANAGER.\n\
  502. Cycle detected:\n\
  503. %s\n\
  504. is the same file as\n"), program_name, pathname);
  505.       *p->pathp = '\0';    /* Truncate pathname.  */
  506.       fprintf (stderr, "%s\n", pathname);
  507.       *p->pathp = '/';    /* Put it back.  */
  508.       if (interactive)
  509.         {
  510.           fprintf (stderr, _("%s: continue? "), program_name);
  511.           if (!yesno ())
  512.         exit (1);
  513.           return 1;
  514.         }
  515.       else
  516.         exit (1);
  517.     }
  518.     }
  519. #endif /* D_INO_IN_DIRENT */
  520.   return 0;
  521. }
  522.  
  523. static void
  524. usage (int status)
  525. {
  526.   if (status != 0)
  527.     fprintf (stderr, _("Try `%s --help' for more information.\n"),
  528.          program_name);
  529.   else
  530.     {
  531.       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
  532.       printf (_("\
  533. Remove (unlink) the FILE(s).\n\
  534. \n\
  535.   -d, --directory       unlink directory, even if non-empty (super-user only)\n\
  536.   -f, --force           ignore nonexistent files, never prompt\n\
  537.   -i, --interactive     prompt before any removal\n\
  538.   -v, --verbose         explain what is being done\n\
  539.   -r, -R, --recursive   remove the contents of directories recursively\n\
  540.       --help            display this help and exit\n\
  541.       --version         output version information and exit\n"));
  542.     }
  543.   exit (status);
  544. }
  545.